package com.evaluation.security;

import java.sql.SQLIntegrityConstraintViolationException;
import java.util.Date;
import java.util.List;
import java.util.Set;

import org.hibernate.exception.DataException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.web.servlet.error.ErrorController;
import org.springframework.context.support.DefaultMessageSourceResolvable;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import com.evaluation.util.RequestUtils;
import com.evaluation.util.ServiceResult;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.ConstraintViolationException;

@Controller
@RequestMapping("/error")
public class BaseErrorPage implements ErrorController {

	private Logger log = LoggerFactory.getLogger(getClass());
	
	private final String ERROR = "org.springframework.boot.web.servlet.error.DefaultErrorAttributes.ERROR";

	//@ExceptionHandler(value = BindException.class)
    @ResponseBody
    public String bindExceptionErrorHandler(BindException ex) throws Exception {
         log.error("bindExceptionErrorHandler info:{}",ex.getMessage());
         String r;
         
         StringBuilder sb = new StringBuilder();
         FieldError fieldError = ex.getFieldError();
         sb.append(fieldError.getDefaultMessage());
         
         
         r = sb.toString();
         
         return r;
    }
	
	@RequestMapping(produces = {MediaType.TEXT_HTML_VALUE, MediaType.TEXT_PLAIN_VALUE})
    public String errorHtml(HttpServletRequest request, HttpServletResponse response, ModelMap map) {
		
		Integer errorCode = (Integer)request.getAttribute("jakarta.servlet.error.status_code");
		
		if( errorCode != null ) {
			switch( errorCode.intValue() ) {
				case HttpServletResponse.SC_UNAUTHORIZED:
					if( !RequestUtils.checkSessionInvalidateForRest(request, HttpServletResponse.SC_UNAUTHORIZED, "登录超时,请重新登录") ) {
			    		return null;
			    	}
					
					return "redirect:/login";
					
				case HttpServletResponse.SC_FORBIDDEN:
					if( !RequestUtils.checkSessionInvalidateForRest(request, HttpServletResponse.SC_UNAUTHORIZED, "登录超时,请重新登录") ) {
			    		return null;
			    	}
					
					break;
			}
		}

		/*Enumeration<String> enm = request.getAttributeNames();
		while( enm.hasMoreElements() ) {
			String ss = enm.nextElement();
			
			System.out.println( ss +"=" + request.getAttribute(ss));
		}*/

		String reslt = resolverErrorMsg(request, true);

		///map.addAttribute("timestamp", LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));
		map.addAttribute("timestamp", new Date());
		map.addAttribute("status", errorCode);
		map.addAttribute("success", false);
		///map.addAttribute("error", request.getAttribute("javax.servlet.error.message"));
		///map.addAttribute("message", reslt == null ? request.getAttribute("javax.servlet.error.message") : reslt );
		
		map.addAttribute("error", request.getAttribute("jakarta.servlet.error.message"));
		map.addAttribute("message", reslt == null ? request.getAttribute("jakarta.servlet.error.message") : reslt );
		//map.addAttribute("trackId", Current.traceId.get());
		
		return getErrorPath();
        
    }
	
	
	@ExceptionHandler(value = BindException.class)
	@RequestMapping(produces = MediaType.APPLICATION_JSON_VALUE)//, headers= {"x-requested-with"}
	@ResponseBody
    public ServiceResult<String> error(HttpServletRequest request, HttpServletResponse response ) {

		ServiceResult<String> sr = new ServiceResult<String>().setFail();
		/**
		Enumeration<String> er = request.getHeaderNames();
		while( er.hasMoreElements() ) {
			var name = er.nextElement();
			System.out.println(name+"\t"+request.getHeader(name));
		}
		
		System.out.println( request.getRequestURI() );
		System.out.println( request.getQueryString() );
		**/
		
		///Integer errorCode = (Integer)request.getAttribute("javax.servlet.error.status_code");
		Integer errorCode = (Integer)request.getAttribute("jakarta.servlet.error.status_code");
		
		String reslt = resolverErrorMsg(request, false);
		
		///sr.setMsg(reslt == null ? (String)request.getAttribute("javax.servlet.error.message") : reslt );
		sr.setMsg(reslt == null ? (String)request.getAttribute("jakarta.servlet.error.message") : reslt );
		///sr.setResult( (String)request.getAttribute("javax.servlet.error.message") );
		sr.setRepCode(errorCode.toString());

		log.error("code:{}, msg:{}", sr.getRepCode(), sr.getMsg() );
		
		return sr;
    }
	
	///@Override
    public String getErrorPath() {
        //log.info("进入自定义错误页面");
        
        return "error";
    }
	
	
	private String resolverErrorMsg( HttpServletRequest request, boolean isHtml ) {
		Object obj = request.getAttribute( ERROR );

		String reslt = (String)request.getAttribute( "SPRING_SECURITY_LAST_EXCEPTION" );
		
		if( obj instanceof BindException ) {
			StringBuilder sb = new StringBuilder();
			BindException bindEx = (BindException)obj;

		//	FieldError fieldError = bindEx.getFieldError();
			List<ObjectError> objList =  bindEx.getAllErrors();
			
			objList.stream().forEach( objectError-> {
				log.error( "{}", objectError );
				sb.append(((DefaultMessageSourceResolvable)objectError.getArguments()[0]).getCode() )
					.append(":").append( objectError.getDefaultMessage() ).append( isHtml ? "<br>" : "; ");
			});
			
			//for (ObjectError objectError : objList) {
				/*
				 * System.out.println(bindEx.getBindingResult().getFieldError());
				 * System.out.println( objectError.getCodes()[0] ); System.out.println(
				 * objectError.getCodes()[1] ); System.out.println( objectError.getCodes()[2] );
				 * 
				 * System.out.println( objectError.getDefaultMessage() ); System.out.println(
				 * objectError.getObjectName() );
				 * System.out.println(objectError.getArguments()[0]); System.out.println( );
				 * System.out.println( objectError.getCode() );
				 */
			//	sb.append(((DefaultMessageSourceResolvable)objectError.getArguments()[0]).getCode() )
			//		.append(":").append( objectError.getDefaultMessage() ).append("; ");
				
			//}
			
			/**
			System.out.println( bindEx.getMessage() );
			System.out.println( fieldError );
			System.out.println( fieldError.getCode() );
			System.out.println( fieldError.getRejectedValue() );
			System.out.println( fieldError.getDefaultMessage() );
			System.out.println( fieldError.getField() );
			**/

	        //sb.append(fieldError.getDefaultMessage());

	       // System.out.println( sb );
	        
	        reslt = sb.toString(); //fieldError.getDefaultMessage();
	        
		}
		else if ( obj instanceof ConstraintViolationException ) {
			StringBuilder sb = new StringBuilder();
			ConstraintViolationException constraintVolEx = (ConstraintViolationException)obj;
			Set<ConstraintViolation<?>> constraintVolSets = constraintVolEx.getConstraintViolations();
			constraintVolSets.stream().forEach( vol -> {
				log.error( "{}", vol );
				
				sb.append( vol.getPropertyPath() )
					.append(":").append( vol.getMessage() ).append( isHtml ? "<br>" : "; ");
			});
			
			reslt = sb.toString();
		}
		else if( obj instanceof DataIntegrityViolationException ) {
			DataIntegrityViolationException dataVolEx = (DataIntegrityViolationException)obj;
			Object causeObj = dataVolEx.getCause();
			if( causeObj != null && causeObj instanceof DataException ) {
				DataException dataEx = (DataException)causeObj;
				
				reslt = dataEx.getCause().getMessage();
			}
			else if( causeObj != null && causeObj instanceof SQLIntegrityConstraintViolationException ) {
				SQLIntegrityConstraintViolationException sqlIntEx = (SQLIntegrityConstraintViolationException)causeObj;
				
				reslt = sqlIntEx.getMessage();
			}
			else {
				reslt = dataVolEx.getMessage();
			}
		 /*处理validate注解 校验的 异常*/
		} else if ( obj instanceof MethodArgumentNotValidException) {
			MethodArgumentNotValidException e 	 = (MethodArgumentNotValidException)obj;
			BindingResult result = e.getBindingResult();
			log.error( "{}", e );
			StringBuffer sb = new StringBuffer();
			for (ObjectError error : result.getAllErrors()) {
				String tempField = error.getCodes()[0];
				String code = error.getDefaultMessage();
				String message = String.format("错误的请求参数:%s，原因:%s", tempField, code);
				sb.append(message);
			}
			reslt = sb.toString();
		}


		else if( reslt == null && obj != null ) {
			reslt = ((Throwable)obj).getMessage();
		}
		
		return reslt;
	}

}
